home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Just Call Me Internet
/
Just Call Me Internet.iso
/
prog
/
atari
/
c
/
nos042_s
/
smtpcli.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-16
|
34KB
|
1,378 lines
/*
* CLIENT routines for Simple Mail Transfer Protocol ala RFC821
* A.D. Barksdale Garbee II, aka Bdale, N3EUA
* Copyright 1986 Bdale Garbee, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
* Modified 14 June 1987 by P. Karn for symbolic target addresses,
* also rebuilt locking mechanism
* Copyright 1987 1988 David Trulli, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*/
/****************************************************************************
* $Id: smtpcli.c 1.18 94/01/04 14:10:21 ROOT_DOS Exp $
* 29 May 92 1.2 GT mailroute (): use gateway if defined.
* 02 Jun 92 1.3 GT Add "smtp wait" and timeout on connect () call
* in smtp_send ().
* 04 Jun 92 1.4 GT Make smtptick () a separate process.
* 05 Jun 92 1.5 GT Timeout on connect () to Gateway.
* 26 Jun 92 Paul@wolf.demon.co.uk Fixed routing for internal
* email (doesn't go via the external gateway)
* 13 Jul 92 1.7 GT Remove 1.6 changes.
* 05 Aug 92 amc@beryl.demon.co.uk changed printfs to tprintf
* 27 Aug 92 1.10 mt@kram.demon.co.uk added smtp separator
* Dec 92 mt@kram.org don't use gateway for any mail MXed via
* us. Add delay channel for local mail
* Add smtp beep on|off
* 19 Apr 93 1.16 GT If newproc () fails, reset tick flag.
* 08 May 93 1.17 GT Fix warnings.
* Fix "smtp list".
* IAY Fix dot transparency as per RFC 821.
* 07 Nov 93 1.18 GT Verbose: show "sending" progress.
* mailroute (): cope with domain literals.
*
* ST NOS Version by David Nash - dnash@chaos.demon.co.uk
*
* __stdargs - smtp_send, smtptick_body
* dosmtplist - call statx to fix path seperator
*
****************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#ifdef ATARI
#include <ext.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#include <time.h>
#include <setjmp.h>
#ifdef UNIX
#include <sys/types.h>
#endif
#ifdef AMIGA
#include <stat.h>
#else
#include <sys/stat.h>
#endif
#ifdef __TURBOC__
#include <dir.h>
#include <io.h>
#endif
#include "global.h"
#include <stdarg.h>
#include "config.h"
#include "domain.h"
#include "mbuf.h"
#include "cmdparse.h"
#include "proc.h"
#include "socket.h"
#include "timer.h"
#include "netuser.h"
#include "smtp.h"
#include "dirutil.h"
#include "commands.h"
#include "session.h"
#include "ip.h"
static struct timer Smtpcli_t;
static int32 Gateway;
static int32 Delaytime = 0;
int near smtpverbose = 1;
char *near smtp_separator = NULL;
int near are_we_an_mx;
static unsigned short Smtptrace = 0; /* used for trace level */
static int dosmtptrace __ARGS ((int argc, char *argv[], void *p));
static unsigned short Smtpmaxcli = MAXSESSIONS; /* the max client connections
* allowed */
static int Smtpsessions = 0; /* number of client connections currently open */
static int Smtpbatch;
int near Smtpmode = 0;
int near Smtpbeep = 0;
int32 near connect_wait_val = 60000L; /* default connection timeout */
static struct smtpcli *cli_session[MAXSESSIONS]; /* queue of client
* sessions */
static void del_job __ARGS ((struct smtp_job * jp));
static void del_session __ARGS ((struct smtpcli * cb));
static int dogateway __ARGS ((int argc, char *argv[], void *p));
static int doseparator __ARGS ((int argc, char *argv[], void *p));
static int dosmtpmaxcli __ARGS ((int argc, char *argv[], void *p));
static int dotimer __ARGS ((int argc, char *argv[], void *p));
static int dodelay __ARGS ((int argc, char *argv[], void *p));
static int dowait __ARGS ((int argc, char *argv[], void *p));
static int dosmtpkill __ARGS ((int argc, char *argv[], void *p));
static int dosmtplist __ARGS ((int argc, char *argv[], void *p));
static int dobatch __ARGS ((int argc, char *argv[], void *p));
static int dobeep __ARGS ((int argc, char *argv[], void *p));
static void execjobs __ARGS ((void));
static int getresp __ARGS ((struct smtpcli * ftp, int mincode));
static void logerr __ARGS ((struct smtpcli * cb, char *line));
static struct smtpcli *lookup __ARGS ((int32 destaddr));
static struct smtpcli *newcb __ARGS ((void));
static int next_job __ARGS ((struct smtpcli * cb));
static void retmail __ARGS ((struct smtpcli * cb));
static void sendcmd __ARGS ((struct smtpcli * cb, char *fmt,...));
static int smtpsendfile __ARGS ((struct smtpcli * cb));
static int setsmtpmode __ARGS ((int argc, char *argv[], void *p));
static struct smtp_job *setupjob __ARGS ((struct smtpcli * cb, char *id, char *from));
static void __stdargs smtp_send __ARGS ((int unused, void *cb1, void *p));
static int smtpkick __ARGS ((int argc, char *argv[], void *p));
static void __stdargs smtptick_body __ARGS ((int unused, void *t, void *p));
static int doverbose __ARGS ((int argc, char *argv[], void *p));
static int should_enter_delay __ARGS ((struct smtpcli * cb));
static int explock __ARGS ((char *dir, char *pre, char *suf, int32 age));
static struct cmds Smtpcmds[] = {
{"batch", dobatch, 0, 0, NULLCHAR},
{"beep", dobeep, 0, 0, NULLCHAR},
{"delay", dodelay, 0, 0, NULLCHAR},
{"gateway", dogateway, 0, 0, NULLCHAR},
{"kick", smtpkick, 0, 0, NULLCHAR},
{"kill", dosmtpkill, 0, 2, "kill <jobnumber>"},
{"list", dosmtplist, 0, 0, NULLCHAR},
{"maxclients", dosmtpmaxcli, 0, 0, NULLCHAR},
{"mode", setsmtpmode, 0, 0, NULLCHAR},
{"separator", doseparator, 0, 0, NULLCHAR},
{"timer", dotimer, 0, 0, NULLCHAR},
{"trace", dosmtptrace, 0, 0, NULLCHAR},
{"wait", dowait, 0, 0, NULLCHAR},
{"verbose", doverbose, 0, 0, NULLCHAR}, /* Actually for Server */
{NULLCHAR},
};
int dosmtp(int argc, char *argv[], void *p)
{
return subcmd (Smtpcmds, argc, argv, p);
}
static int dobatch(int argc, char *argv[], void *p)
{
return setbool (&Smtpbatch, "SMTP batching", argc, argv);
}
static int dobeep(int argc, char *argv[], void *p)
{
return setbool (&Smtpbeep, "SMTP beep on delivery", argc, argv);
}
static int dosmtpmaxcli(int argc, char *argv[], void *p)
{
return setshort (&Smtpmaxcli, "Max clients", argc, argv);
}
static int setsmtpmode(int argc, char *argv[], void *p)
{
if (argc < 2) {
tprintf ("smtp mode: %s\n", (Smtpmode & QUEUE) ? "queue" : "route");
}
else {
switch (*argv[1]) {
case 'q':
Smtpmode |= QUEUE;
break;
case 'r':
Smtpmode &= ~QUEUE;
break;
default:
tprintf ("Usage: smtp mode [queue | route]\n");
break;
}
}
return 0;
}
static int dogateway(int argc, char *argv[], void *p)
{
int32 n;
if (argc < 2)
tprintf ("%s\n", inet_ntoa (Gateway));
else
if ((n = resolve (argv[1])) == 0) {
tprintf (Badhost, argv[1]);
return 1;
}
else
Gateway = n;
log(-1, "dogateway: resolve gateway = %x ", (void *)Gateway); /* DEBUG */
return 0;
}
static int doseparator(int argc, char *argv[], void *p)
{
int i;
char *t;
if (argc < 2) {
if (smtp_separator) {
log(-1, "doseparator: free smtp_separator = %x ",(void *)smtp_separator); /* DEBUG */
free(smtp_separator);
}
smtp_separator = NULL;
}
else {
t = malloc(81);
log(-1, "doseperator: malloc t = %x ", (void *)t); /* DEBUG */
if (t == NULL)
return 1;
t[0] = '\0';
for (i = 1; i < argc; i++) {
strcat (t, argv[i]);
if (i < (argc - 1))
strcat (t, " ");
}
if ((smtp_separator = malloc(strlen (t) + 1)) == NULL) {
log(-1, "doseperator: free t = %x ", (void *)t); /* DEBUG */
free(t);
return 1;
}
log(-1, "doseperator: malloc smtp_separator = %x ", (void *)smtp_separator); /* DEBUG */
strcpy(smtp_separator, t);
log(-1, "doseperator: free t = %x ", (void *)t); /* DEBUG */
free(t);
}
return 0;
}
static int dosmtptrace(int argc, char *argv[], void *p)
{
return setshort (&Smtptrace, "SMTP tracing", argc, argv);
}
/* list jobs wating to be sent in the mqueue */
static int dosmtplist (int argc, char *argv[], void *p)
{
char tstring[80];
char line[20];
char host[LINELEN];
char to[LINELEN];
char from[LINELEN];
char *cp;
char status;
struct stat stbuf;
struct tm *tminfo;
FILE *fp;
int statx(const char *, struct stat *);
Current->flowmode = 1; /* Enable the more mechanism */
tprintf ("S Job Size Date Time Host From\n");
filedir (Mailqueue, 0, line);
while (line[0] != '\0') {
(void) sprintf (tstring, "%s/%s", Mailqdir, line);
if ((fp = fopen (tstring, READ_TEXT)) == NULLFILE) {
tprintf ("Can't open %s: %s\n", tstring, sys_errlist[errno]);
pwait (NULL);
filedir (Mailqueue, 1, line);
continue;
}
if ((cp = strrchr (line, '.')) != NULLCHAR)
*cp = '\0';
(void) sprintf (tstring, "%s/%s.lck", Mailqdir, line);
if (access (tstring, 0))
status = ' ';
else
status = 'L';
/* Now see if it's in delay */
if (status == ' ') {
sprintf (tstring, "%s/%s.dly", Mailqdir, line);
if (!access (tstring, 0))
status = 'D';
}
(void) sprintf (tstring, "%s/%s.txt", Mailqdir, line);
if (statx (tstring, &stbuf) == -1) { /* ATARI statx to fix path sep */
tprintf ("Can't stat %s: %s\n", tstring, sys_errlist[errno]);
pwait (NULL);
filedir (Mailqueue, 1, line);
continue;
}
tminfo = localtime (&stbuf.st_ctime);
fgets (host, sizeof (host), fp);
rip (host);
fgets (from, sizeof (from), fp);
rip (from);
tprintf ("%c %7s %7ld %02d/%02d %02d:%02d %-20s %s\n ",
status, line, stbuf.st_size,
tminfo->tm_mon + 1,
tminfo->tm_mday,
tminfo->tm_hour,
tminfo->tm_min,
host, from);
while (fgets (to, sizeof (to), fp) != NULLCHAR) {
rip (to);
tprintf ("%s ", to);
}
tprintf ("\n");
(void) fclose (fp);
pwait (NULL);
filedir (Mailqueue, 1, line);
}
Current->flowmode = 0;
return 0;
}
/* dosmtpkill - kill a job in the mqueue */
static int dosmtpkill(int argc, char *argv[], void *p)
{
char s[SLINELEN];
char *cp, c;
sprintf (s, "%s/%s.lck", Mailqdir, argv[1]);
cp = strrchr (s, '.');
if (!access (s, 0)) {
Current->ttystate.echo = Current->ttystate.edit = 0;
c = keywait("Warning, the job is locked by SMTP. Remove (y/n)? ", 0);
Current->ttystate.echo = Current->ttystate.edit = 1;
if (c != 'y')
return 0;
(void)unlink(s);
}
strcpy(cp, ".wrk");
if (unlink(s))
tprintf("Job id %s not found\n", argv[1]);
strcpy(cp, ".txt");
(void)unlink(s);
strcpy(cp, ".dly");
(void)unlink(s);
return 0;
}
/* dotimer - Set outbound spool scan interval */
static int dotimer(int argc, char *argv[], void *p)
{
if (argc < 2) {
tprintf ("%lu/%lu\n",
read_timer (&Smtpcli_t) / 1000L,
dur_timer (&Smtpcli_t) / 1000L);
return 0;
}
Smtpcli_t.func = (void (*) ()) smtptick; /* what to call on timeout */
Smtpcli_t.arg = NULL; /* dummy value */
set_timer (&Smtpcli_t, atol (argv[1]) * 1000L); /* set timer duration */
start_timer (&Smtpcli_t); /* and fire it up */
return 0;
}
/* dodelay - Set delay channel time */
static int dodelay(int argc, char *argv[], void *p)
{
if (argc < 2) {
tprintf ("%lu\n", Delaytime);
return 0;
}
Delaytime = atol (argv[1]);
return 0;
}
/* dowait - Displays or sets the connection wait timeout. */
static int dowait(int argc, char *argv[], void *p)
{
if (argc < 2) {
tprintf ("smtp connection timeout: %lu\n", connect_wait_val / 1000L);
return (0);
}
connect_wait_val = atol (argv[1]) * 1000L; /* set timeout */
return (0);
}
static int smtpkick(int argc, char *argv[], void *p)
{
int32 addr = 0;
if (argc > 1 && (addr = resolve (argv[1])) == 0) {
tprintf (Badhost, argv[1]);
return 1;
}
log(-1, "smtpkick: resolve addr = %x ", (void *)addr); /* DEBUG */
smtptick ((void *) addr);
return 0;
}
/* This is the routine that gets called every so often to do outgoing
* mail processing. When called with a null argument, it runs the entire
* queue; if called with a specific non-zero IP address from the remote
* kick server, it only starts up sessions to that address.
*/
static char smtptick_running = 0;
int smtptick(void *t) {
if (smtptick_running != 0)
return (0);
smtptick_running = 1;
if (newproc ("smtp_tick", 1024, smtptick_body, 0, t, NULL, 0) == NULLPROC) {
start_timer (&Smtpcli_t); /* wait a while */
smtptick_running = 0;
}
return (0);
}
static void __stdargs smtptick_body (int unused, void *t, void *p)
{
struct smtpcli *cb;
struct smtp_job *jp;
struct list *ap;
char tmpstring[LINELEN], wfilename[13], prefix[9];
char from[LINELEN], to[LINELEN];
char *cp, *cp1;
int32 destaddr, target, num_files;
FILE *wfile;
target = (int32) t;
if (Smtptrace > 5)
tprintf ("smtp daemon entered, target = %s\n", inet_ntoa (target));
if (availmem () < Memthresh) {
/* Memory is tight, don't do anything */
/* Restart timer */
start_timer (&Smtpcli_t);
smtptick_running = 0;
return;
}
/* First see how many there are now */
num_files = 0;
for (filedir (Mailqueue, 0, wfilename); wfilename[0] != '\0';
filedir (Mailqueue, 1, wfilename))
num_files++;
/* Try to deal with that many messages, no more or we may loop forever */
for (filedir (Mailqueue, 0, wfilename); wfilename[0] != '\0' && num_files--;
filedir (Mailqueue, 1, wfilename)) {
/* save the prefix of the file name which it job id */
cp = wfilename;
cp1 = prefix;
while (*cp && *cp != '.')
*cp1++ = *cp++;
*cp1 = '\0';
/* lock this file from the smtp daemon */
if (mlock (Mailqdir, prefix))
continue;
/* Try to bring the job outof delay (in case it's in delay) */
explock (Mailqdir, prefix, "dly", Delaytime);
/* Check that it's not in delay */
if (mlock_suffix (Mailqdir, prefix, "dly")) {
rmlock (Mailqdir, prefix);
continue;
}
else
rmlock_suffix (Mailqdir, prefix, "dly");
sprintf (tmpstring, "%s/%s", Mailqdir, wfilename);
if ((wfile = fopen (tmpstring, READ_TEXT)) == NULLFILE) {
/* probably too many open files */
(void) rmlock (Mailqdir, prefix);
/* continue to next message. The failure may be temporary */
continue;
}
(void) fgets (tmpstring, LINELEN, wfile);/* read target host */
rip (tmpstring);
if ((destaddr = mailroute (tmpstring, Ip_addr)) == 0) {
fclose (wfile);
tprintf ("** smtp: Unknown address %s\n", tmpstring);
(void) rmlock (Mailqdir, prefix);
continue;
}
if (target != 0 && destaddr != target) {
fclose (wfile);
(void) rmlock (Mailqdir, prefix);
continue; /* Not the proper target of a kick */
}
if ((cb = lookup (destaddr)) == NULLSMTPCLI) {
/* there are enough processes running already */
if (Smtpsessions >= Smtpmaxcli) {
if (Smtptrace)
tprintf ("smtp daemon: too many processes\n");
fclose (wfile);
(void) rmlock (Mailqdir, prefix);
break;
}
if ((cb = newcb()) == NULLSMTPCLI) {
fclose(wfile);
(void)rmlock(Mailqdir, prefix);
break;
}
cb->ipdest = destaddr;
cb->destname = strdup(tmpstring); /* implicit malloc */
log(-1, "smtptick_body: strdup cb->destname = %x ", (void *)cb->destname); /* DEBUG */
}
else {
if (cb->lock) {
/* This system is already is sending mail lets not
* interfere with its send queue. */
fclose (wfile);
(void) rmlock (Mailqdir, prefix);
continue;
}
}
(void) fgets (from, LINELEN, wfile); /* read from */
rip (from);
if ((jp = setupjob(cb, prefix, from)) == NULLJOB) {
fclose(wfile);
rmlock(Mailqdir, prefix);
del_session(cb);
break;
}
while (fgets(to, LINELEN, wfile) != NULLCHAR) {
rip(to);
if (addlist(&jp->to, to, DOMAIN) == NULLLIST) {
fclose(wfile);
del_session(cb);
}
log(-1, "smpttick_body: addlist jp->to = %x ", jp->to); /* DEBUG */
log(-1, "smpttick_body: addlist jp->to->val = %x ", jp->to->val); /* DEBUG */
}
fclose(wfile);
if (Smtptrace > 1) {
tprintf("queue job %s From: %s To:", prefix, from);
for (ap = jp->to; ap != NULLLIST; ap = ap->next)
tprintf(" %s", ap->val);
tprintf ("\n");
}
if (smtpverbose != 0 && Smtptrace <= 1) {
tprintf ("Sending %s From: %s To:", prefix, from);
for (ap = jp->to; ap != NULLLIST; ap = ap->next)
tprintf (" %s", ap->val);
tprintf ("\n");
}
}
/* start sending that mail */
execjobs ();
/* Restart timer */
start_timer(&Smtpcli_t);
smtptick_running = 0;
return;
}
static int should_enter_delay(struct smtpcli *cb)
{
resolve_mx (cb->destname);
log(-1, "should_enter_delay: resolve_mx cb->destname = %x ", (void *)cb->destname); /* DEBUG */
if (are_we_an_mx)
return 1;
return 0;
}
void delay_job(int32 msgid)
{
char t[20];
if (Delaytime == 0)
/* Not using SMTP delay */
return;
sprintf (t, "%lu", msgid);
if (Smtptrace)
tprintf ("Delivery Failed: adding job %s to delay queue\n", t);
mlock_suffix (Mailqdir, t, "dly");
}
/* This is the master state machine that handles a single SMTP transaction.
* It is called with a queue of jobs for a particular host.
* The logic is complicated by the "Smtpbatch" variable, which controls
* the batching of SMTP commands. If Smtpbatch is true, then many of the
* SMTP commands are sent in one swell foop before waiting for any of
* the responses. Unfortunately, this breaks many brain-damaged SMTP servers
* out there, so provisions have to be made to operate SMTP in lock-step mode.
*/
static void __stdargs smtp_send (int unused, void *cb1, void *p)
{
struct smtpcli *cb;
struct list *tp;
struct sockaddr_in fsocket;
char *cp;
int rcode;
int rcpts;
int goodrcpt;
int i;
int init = 1;
cb = (struct smtpcli *)cb1;
cb->lock = 1;
fsocket.sin_family = AF_INET;
fsocket.sin_addr.s_addr = cb->ipdest;
fsocket.sin_port = IPPORT_SMTP;
cb->s = socket(AF_INET, SOCK_STREAM, 0);
sockmode(cb->s, SOCK_ASCII);
setflush(cb->s, -1); /* We'll explicitly flush before reading */
if (Smtptrace)
tprintf ("SMTP client Trying...\n");
/* Run connect () under a timeout in case the destination is unreachable. */
alarm(connect_wait_val);
if (connect (cb->s, (char *) &fsocket, SOCKSIZE) == 0) {
alarm (0L);
if (Smtptrace)
tprintf ("Connected\n");
}
else {
alarm(0L);
if (Delaytime && (cb->ipdest == Ip_addr || should_enter_delay(cb)))
delay_job(atol(cb->jobq->jobname));
else
if (Gateway != 0L) {
/* Try sending it via the gateway, as long as it's not for us. */
if (Smtptrace)
tprintf ("SMTP client Trying gateway...\n");
close_s(cb->s);
cb->ipdest = Gateway;
fsocket.sin_addr.s_addr = Gateway;
cb->s = socket(AF_INET, SOCK_STREAM, 0);
sockmode(cb->s, SOCK_ASCII);
setflush(cb->s, -1);
alarm(connect_wait_val);
if (connect(cb->s, (char *) &fsocket, SOCKSIZE) != 0) {
alarm(0L);
cp = sockerr(cb->s);
if (Smtptrace)
tprintf("Connect failed: %s\n", cp != NULLCHAR ? cp : "");
log(cb->s, "SMTP %s Connect failed: %s", psocket (&fsocket),
cp != NULLCHAR ? cp : "");
goto quit;
}
else {
alarm(0L);
if (Smtptrace)
tprintf ("Connected to Gateway\n");
}
}
else {
cp = sockerr(cb->s);
if (Smtptrace)
tprintf ("Connect failed: %s\n", cp != NULLCHAR ? cp : "");
log (cb->s, "SMTP %s Connect failed: %s", psocket(&fsocket),
cp != NULLCHAR ? cp : "");
goto quit;
}
}
if (!Smtpbatch) {
rcode = getresp (cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
/* Say HELO */
sendcmd(cb, "HELO %s\n", Hostname);
if (!Smtpbatch) {
rcode = getresp(cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
do { /* For each message... */
/* if this file open fails, skip it */
if ((cb->tfile = fopen(cb->tname, READ_TEXT)) == NULLFILE)
continue;
/* Send MAIL and RCPT commands */
sendcmd (cb, "MAIL FROM:<%s>\n", cb->jobq->from);
if (!Smtpbatch) {
rcode = getresp(cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
rcpts = 0;
goodrcpt = 0;
for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next) {
sendcmd(cb, "RCPT TO:<%s>\n", tp->val);
if (!Smtpbatch) {
rcode = getresp (cb, 200);
if (rcode == -1)
goto quit;
if (rcode < 400)
goodrcpt = 1; /* At least one good */
}
rcpts++;
}
/* Send DATA command */
sendcmd(cb, "DATA\n");
if (!Smtpbatch) {
rcode = getresp(cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
if (Smtpbatch) {
/* Now wait for the responses to come back. The first time we
* do this, we wait first for the start banner and HELO
* response. In any case, we wait for the response to the MAIL
* command here. */
for (i = init ? 3 : 1; i > 0; i--) {
rcode = getresp(cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
init = 0;
/* Now process the responses to the RCPT commands */
for (i = rcpts; i != 0; i--) {
rcode = getresp (cb, 200);
if (rcode == -1)
goto quit;
if (rcode < 400)
goodrcpt = 1; /* At least one good */
}
/* And finally get the response to the DATA command. Some
* servers will return failure here if no recipients are valid,
* some won't. */
rcode = getresp (cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
/* check for no good rcpt on the list */
if (goodrcpt == 0) {
sendcmd(cb, ".\n"); /* Get out of data mode */
goto quit;
}
}
/* Send the file. This also closes it */
smtpsendfile(cb);
/* Wait for the OK response */
rcode = getresp(cb, 200);
if (rcode == -1)
goto quit;
if ((rcode >= 200 && rcode < 300) || rcode >= 500) {
/* if a good transfer or permanent failure remove job */
if (cb->errlog != NULLLIST)
retmail (cb);
/* Unlink the textfile */
(void) unlink (cb->tname);
(void) unlink (cb->wname); /* unlink workfile */
log(cb->s, "SMTP sent job %s To: %s From: %s",
cb->jobq->jobname, cb->jobq->to->val, cb->jobq->from);
if (smtpverbose != 0 && Smtptrace <= 1) {
/* Show sending progress. */
tprintf("Sent %s\n", cb->jobq->jobname);
}
}
} while (next_job(cb));
quit:
sendcmd(cb, "QUIT\n");
if (cb->errlog != NULLLIST) {
retmail (cb);
unlink (cb->wname); /* unlink workfile */
unlink (cb->tname); /* unlink text */
}
close_s(cb->s);
if (cb->tfile != NULLFILE)
fclose(cb->tfile);
cb->lock = 0;
del_session(cb);
}
/* mlock - create mail lockfile */
int mlock(char *dir, char *id)
{
return mlock_suffix (dir, id, "lck");
}
int mlock_suffix(char *dir, char *id, char *suffix)
{
char lockname[LINELEN];
int fd;
#if defined(MSDOS) || defined(ATARI)
if (strlen (id) > 8) { /* truncate long filenames */
id[8] = '\0';
if (id[7] == '/')
id[7] = '\0';
}
#endif
/* Try to create the lock file in an atomic operation */
sprintf(lockname, "%s/%s.%s", dir, id, suffix);
if ((fd = open(lockname, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1)
return -1;
close(fd);
return 0;
}
/* rmlock - Remove mail lockfile */
int rmlock(char *dir, char *id)
{
return rmlock_suffix(dir, id, "lck");
}
int rmlock_suffix(char *dir, char *id, char *suffix)
{
char lockname[LINELEN];
#if defined(MSDOS) || defined(ATARI)
if (strlen (id) > 8) { /* truncate long filenames */
id[8] = '\0';
if (id[7] == '/')
id[7] = '\0';
}
#endif
sprintf (lockname, "%s/%s.%s", dir, id, suffix);
return (unlink (lockname));
}
/* del_session - Free the message struct and data */
static void del_session(struct smtpcli *cb)
{
int i;
struct smtp_job *jp, *tp;
if (cb == NULLSMTPCLI)
return;
for (i = 0; i < MAXSESSIONS; i++)
if (cli_session[i] == cb) {
cli_session[i] = NULLSMTPCLI;
break;
}
log(-1,"del_session: free cb->wname = %x ", (void *)cb->wname); /* DEBUG */
free(cb->wname);
log(-1,"del_session: free cb->tname = %x ", (void *)cb->tname); /* DEBUG */
free(cb->tname);
log(-1,"del_session: free cb->destname = %x ", (void *)cb->destname); /* DEBUG */
free(cb->destname);
for (jp = cb->jobq; jp != NULLJOB; jp = tp) {
tp = jp->next;
del_job(jp);
}
del_list(cb->errlog);
log(-1,"del_session: free cb = %x ", (void *)cb); /* DEBUG */
free((void *)cb);
Smtpsessions--; /* number of connections active */
}
/* del_job - Free smtp_job structure */
static void del_job(struct smtp_job *jp)
{
if (*jp->jobname != '\0')
(void) rmlock(Mailqdir, jp->jobname);
log(-1,"del_job: free jp->from = %x ", (void *)jp->from); /* DEBUG */
free(jp->from);
del_list(jp->to);
log(-1,"del_job: free jp = %x ", (void *)jp); /* DEBUG */
free((void *)jp);
}
/* del_list - Delete a list of list structs */
void del_list(struct list *lp)
{
struct list *tp, *tp1;
for (tp = lp; tp != NULLLIST; tp = tp1) {
tp1 = tp->next;
log(-1, "del_list: free tp->val = %x ", (void *)tp->val); /* DEBUG */
free(tp->val);
log(-1, "del_list: free tp = %x ", (void *)tp); /* DEBUG */
free((char *) tp);
}
}
/* retmail - Stub for calling mdaemon to return message to sender */
static void retmail(struct smtpcli *cb)
{
FILE *infile;
if (Smtptrace > 5)
tprintf("smtp job %s returned to sender\n", cb->wname);
if ((infile = fopen(cb->tname, READ_TEXT)) == NULLFILE)
return;
mdaemon(infile, cb->jobq->from, cb->errlog, 1);
fclose(infile);
}
/* lookup - Look to see if a smtp control block exists for this ipdest */
static struct smtpcli *lookup(int32 destaddr)
{
int i;
for (i = 0; i < MAXSESSIONS; i++) {
if (cli_session[i] == NULLSMTPCLI)
continue;
if (cli_session[i]->ipdest == destaddr)
return cli_session[i];
}
return NULLSMTPCLI;
}
/* newcb - create a new smtp control block */
static struct smtpcli *newcb(void)
{
int i;
struct smtpcli *cb;
for (i = 0; i < MAXSESSIONS; i++) {
if (cli_session[i] == NULLSMTPCLI) {
cb = (struct smtpcli *) calloc(1, sizeof (struct smtpcli));
log(-1,"newcb: calloc cb = %x ", (void *)cb); /*DEBUG */
cb->wname = malloc((unsigned) strlen (Mailqdir) + JOBNAME);
log(-1,"newcb: malloc cb->wname = %x ", (void *)cb->wname); /* DEBUG */
cb->tname = malloc((unsigned) strlen (Mailqdir) + JOBNAME);
log(-1,"newcb: malloc cb->tname = %x ", (void *)cb->tname); /* DEBUG */
cli_session[i] = cb;
Smtpsessions++; /* number of connections active */
return (cb);
}
}
return NULLSMTPCLI;
}
static void execjobs(void)
{
int i;
struct smtpcli *cb;
for (i = 0; i < MAXSESSIONS; i++) {
cb = cli_session[i];
if (cb == NULLSMTPCLI)
continue;
if (cb->lock)
continue;
sprintf(cb->tname, "%s/%s.txt", Mailqdir, cb->jobq->jobname);
sprintf(cb->wname, "%s/%s.wrk", Mailqdir, cb->jobq->jobname);
newproc("smtp_send", 1024, smtp_send, 0, cb, NULL, 0);
if (Smtptrace)
tprintf("Trying Connection to %s\n", inet_ntoa (cb->ipdest));
}
}
/* smtp_job - Add this job to control block queue */
static struct smtp_job *setupjob(struct smtpcli *cb, char *id, char *from)
{
struct smtp_job *p1, *p2;
p1 = (struct smtp_job *) calloc(1, sizeof (struct smtp_job));
log(-1, "setupjob: calloc p1 = %x ", (void *)p1); /* DEBUG */
p1->from = strdup(from); /* implicit malloc */
log(-1, "setupjob: strdup p1->from = %x ", (void *)p1->from); /* DEBUG */
strcpy (p1->jobname, id);
/* now add to end of jobq */
if ((p2 = cb->jobq) == NULLJOB)
cb->jobq = p1;
else {
while (p2->next != NULLJOB)
p2 = p2->next;
p2->next = p1;
}
return p1;
}
/* next_job - Called to advance to the next job */
static int next_job(struct smtpcli *cb)
{
struct smtp_job *jp;
jp = cb->jobq->next;
del_job(cb->jobq);
/* remove the error log of previous message */
del_list(cb->errlog);
cb->errlog = NULLLIST;
cb->jobq = jp;
if (jp == NULLJOB)
return 0;
sprintf(cb->tname, "%s/%s.txt", Mailqdir, jp->jobname);
sprintf(cb->wname, "%s/%s.wrk", Mailqdir, jp->jobname);
if (Smtptrace > 5)
tprintf("sending job %s\n", jp->jobname);
return 1;
}
/* mailroute - Mail routing function. */
int32 mailroute(char *dest, int32 gateway)
{
int32 destaddr;
/* Test for domain literal first. */
if (*dest == '[')
return (aton(dest));
/* If the destination is this site, then *don't* go through a mail
* server (MX site)
*/
if (stricmp(dest, Hostname) == 0)
if ((destaddr = resolve(dest)) != 0) {
log(-1, "next_job: resolve destaddr = %x ", (void *)destaddr); /* DEBUG */
return destaddr;
}
log(-1, "next_job: resolve destaddr = %x ", (void *)destaddr); /* DEBUG */
/* look up address or use the gateway */
destaddr = resolve_mx(dest);
log(-1, "next_job: resolve_mx destaddr = %x ", (void *)destaddr); /* DEBUG */
if (destaddr == 0 && (destaddr = resolve (dest)) == 0) {
#ifdef DSERVER
if (zone_filename (dest, NULL))
destaddr = gateway;
else
#endif
if (Gateway != 0)
destaddr = Gateway; /* Use the gateway */
}
return destaddr;
}
/* logerr - Save line in error list */
static void logerr(struct smtpcli *cb, char *line)
{
struct list *lp, *tp;
tp = (struct list *)calloc(1, sizeof (struct list));
log(-1, "logerr: calloc tp = %x ", (void *)tp); /* DEBUG */
tp->val = strdup(line); /* implicit malloc */
log(-1, "logerr: strdup tp->val = %x ", (void *)tp->val); /* DEBUG */
/* find end of list */
if ((lp = cb->errlog) == NULLLIST)
cb->errlog = tp;
else {
while (lp->next != NULLLIST)
lp = lp->next;
lp->next = tp;
}
}
static int smtpsendfile(struct smtpcli *cb)
{
int error = 0;
strcpy(cb->buf, "\n");
while (fgets (cb->buf, sizeof (cb->buf), cb->tfile) != NULLCHAR) {
if (cb->buf[0] == '.') /* handle dot transparency */
usputc(cb->s,'.'); /* add extra '.' */
usputs (cb->s, cb->buf);
}
fclose(cb->tfile);
cb->tfile = NULLFILE;
/* Send the end-of-message command */
if (cb->buf[strlen (cb->buf) - 1] == '\n')
sendcmd(cb, ".\n");
else
sendcmd(cb, "\n.\n");
return error;
}
/* sendcmd - Do a printf() on the socket with optional local tracing */
static void sendcmd(struct smtpcli * cb, char *fmt,...)
{
va_list args;
va_start(args, fmt);
if (Smtptrace) {
vsprintf(cb->buf, fmt, args);
tprintf("smtp sent: %s", cb->buf);
}
vsprintf(cb->buf, fmt, args);
usputs(cb->s, cb->buf);
va_end(args);
}
/* getresp - Wait for, read & display server response. Return the result code. */
static int getresp(struct smtpcli *cb, int mincode)
{
int rval;
char line[LINELEN];
usflush (cb->s);
for (;;) {
/* Get line */
if (recvline(cb->s, line, LINELEN) == -1) {
rval = -1;
break;
}
rip(line); /* Remove cr/lf */
rval = atoi(line);
if (Smtptrace)
tprintf("smtp recv: %s\n", line); /* Display to user */
if (rval >= 500) { /* Save permanent error replies */
char tmp[LINELEN];
if (cb->errlog == NULLLIST) {
sprintf(tmp, "While talking to %s:",
cb->destname);
logerr(cb, tmp);
}
if (cb->buf[0] != '\0') { /* save offending command */
rip(cb->buf);
sprintf(tmp, ">>> %s", cb->buf);
logerr(cb, tmp);
cb->buf[0] = '\0';
}
sprintf(tmp, "<<< %s", line);
logerr(cb, tmp); /* save the error reply */
}
/* Messages with dashes are continued */
if (line[3] != '-' && rval >= mincode)
break;
}
return rval;
}
static int doverbose(int argc, char *argv[], void *p)
{
return setbool(&smtpverbose, "SMTP verbose mode", argc, argv);
}
unsigned long golden_ymdhms(long year, long month, long day, long hour, long minute, long second)
{
long result = 365L * year + day + 31L * (month - 1);
if (month < 3)
result += (year - 1L) / 4L - (75L * ((year - 1) / 100 + 1)) / 100L;
else
result = result - (40L * month + 230L) / 100L + year / 4L - (75L * (year / 100 + 1)) / 100L;
result *= 86400L;
result += 3600 * hour;
result += 60 * minute;
result += second;
return result;
}
static int explock(char *dir, char *pre, char *suf, int32 age)
{
union FILE_TIME {
struct ftime ft;
unsigned int ft_int[2];
} file_time;
struct ffblk fileinfo;
struct tm *tm_now;
time_t t_now;
char t[80];
unsigned long now, fage, difference;
sprintf(t, "%s/%s.%s", dir, pre, suf);
if (findfirst (t, &fileinfo, 0) == -1)
return 1;
if (Delaytime == 0)
return !unlink (t);
t_now = time(NULL);
tm_now = localtime(&t_now);
now = golden_ymdhms(tm_now->tm_year + 1900, tm_now->tm_mon + 1,
tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min,
tm_now->tm_sec);
file_time.ft_int[0] = fileinfo.ff_ftime;
file_time.ft_int[1] = fileinfo.ff_fdate;
fage = golden_ymdhms (file_time.ft.ft_year + 1980, file_time.ft.ft_month,
file_time.ft.ft_day, file_time.ft.ft_hour, file_time.ft.ft_min,
file_time.ft.ft_tsec * 2);
difference = now - fage;
if (difference >= Delaytime)
return !unlink(t);
return 0;
}